/* ibm1130_sca.c: IBM 1130 synchronous communications adapter emulation

   Based on the SIMH simulator package written by Robert M Supnik

   Brian Knittel
   Revision History

   2005.03.08 - Started

 * (C) Copyright 2005-2010, Brian Knittel.
 * You may freely use this program, but: it offered strictly on an AS-IS, AT YOUR OWN
 * RISK basis, there is no warranty of fitness for any purpose, and the rest of the
 * usual yada-yada. Please keep this notice and the copyright in any distributions
 * or modifications.
 *
 * This is not a supported product, but I welcome bug reports and fixes.
 * Mail to simh@ibm1130.org
 */

/******************************************************************************************************************
 * NOTES:
 *          This module sends raw bisync data over a standard TCP port. It's meant to be
 *          used with the emulated 2703 device in Hercules.
 *
 * Attach command:
 *
 * to establish an outgoing connection:
 *
 *      attach sca host                 connect to named host on default port (initial default port is 2703); or
 *      attach sca host:###             connect to named host on port ###. ### is also set as new default port number.
 *                                      >> The simulator waits until the connection is established
 *
 * or to set up for incoming connections:
 *
 *      attach sca -l dummy             listen for a connection on default port (initially 2703); Nonnumeric "dummy" argument is ignored; or
 *      attach sca -l ###               listen for a connection on the port ###. ### is also set as the new default port
 *                                      >> The simulator proceeds. When another simulator connects, the READY bit is set in the DSW.
 *
 *                                      If the SCA's autoanswer-enable bit has been set, an incoming connection causes an interrupt (untested)
 * Configuration commands:
 *
 *      set sca bsc                     set bisync mode (default)
 *      set sca str                     set synchronous transmit/recieve mode (NOT IMPLEMENTED)
 *
 *      set sca ###                     set simulated baud rate to ###, where ### is 600, 1200, 2000, 2400 or 4800 (4800 is default)
 *
 *      set sca half                    set simulated half-duplex mode
 *      set sca full                    set simulated full-duplex mode (note: 1130's SCA can't actually send and receive at the same time!)
 *
 *      deposit sca keepalive ###       send SYN packets every ### msec when suppressing SYN's, default is 0 (no keepalives)
 *
 * STR/BSC mode is selected by a toggle switch on the 1130, with the SET SCA BSC or SET SET STR command here.
 * Testable with in_bsc_mode() or in_str_mode() in this module. If necessary, the SET command can be configured
 * to call a routine when the mode is changed; or, we can just required the user to reboot the simulated 1130
 * when switching modes.
 *
 * STR MODE IS NOT IMPLEMENTED!
 *
 * The SCA adapter appears to know nothing of the protocols used by STR and BSC. It does handle the sync/idle
 * character specially, and between BSC and STR mode the timers are used differently. Also in STR mode it
 * can be set to a sychronization mode where it sends SYN's without program intervention.
 *
 * See definition of SCA_STATE for defintion of simulator states.
 *
 * Rather than trying to simulate the actual baud rates, we try to keep the character service interrupts
 * coming at about the same number of instruction intervals -- thus existing 1130 code should work correctly
 * but the effective data transfer rate will be much higher. The "timers" however are written to run on real wall clock
 * time, This may or may not work. If necessary they could be set to time out based on the number of calls to sca_svc
 * which occurs once per character send/receive time; For example, at 4800 baud and an 8 bit frame, we get
 * 600 characters/second, so the 3 second timer would expire in 1800 sca_svc calls. Well, that's something to
 * think about.
 *
 * To void blowing zillions of SYN characters across the network when the system is running but idle, we suppress
 * them. If 100 consecutive SYN's are sent, we flush the output buffer and stop sending SYN's
 * until some other character is sent, OR the line is turned around (e.g. INITR, INITW or an end-operation
 * CONTROL is issued), or the number of msec set by DEPOSIT SCS KEEPALIVE has passed, if a value has
 * been set. By default no keepalives are sent.
 *
 * Timer operations are not debugged. First, do timers automatically reset and re-interrupt if
 * left alone after they timeout the first time? Does XIO_SENSE_DEV really restart all running timers?
 * Does it touch the timer trigger (program timer?) How do 3 and 1.25 second timers really work
 * in BSC mode? Hard to tell from the FC manual.
 ******************************************************************************************************************/

#include "ibm1130_defs.h"
#include "sim_sock.h"                                       /* include path must include main simh directory */

#define DEBUG_SCA_FLUSH         0x0001                      /* debugging options */
#define DEBUG_SCA_TRANSMIT      0x0002
#define DEBUG_SCA_CHECK_INDATA  0x0004
#define DEBUG_SCA_RECEIVE_SYNC  0x0008
#define DEBUG_SCA_RECEIVE_DATA  0x0010
#define DEBUG_SCA_XIO_READ      0x0020
#define DEBUG_SCA_XIO_WRITE     0x0040
#define DEBUG_SCA_XIO_CONTROL   0x0080
#define DEBUG_SCA_XIO_INITW     0x0100
#define DEBUG_SCA_XIO_INITR     0x0200
#define DEBUG_SCA_XIO_SENSE_DEV 0x0400
#define DEBUG_SCA_TIMERS        0x0800
#define DEBUG_SCA_ALL           0xFFFF

/* #define DEBUG_SCA            (DEBUG_SCA_TIMERS|DEBUG_SCA_FLUSH|DEBUG_SCA_TRANSMIT|DEBUG_SCA_CHECK_INDATA|DEBUG_SCA_RECEIVE_SYNC|DEBUG_SCA_RECEIVE_DATA|DEBUG_SCA_XIO_INITR|DEBUG_SCA_XIO_INITW) */
#define DEBUG_SCA           (DEBUG_SCA_TIMERS|DEBUG_SCA_FLUSH|DEBUG_SCA_CHECK_INDATA|DEBUG_SCA_XIO_INITR|DEBUG_SCA_XIO_INITW)

#define SCA_DEFAULT_PORT        "2703"                      /* default socket, This is the number of the IBM 360's BSC device */

#define MAX_SYNS                 100                        /* number of consecutive syn's after which we stop buffering them */

/***************************************************************************************
 *  SCA
 ***************************************************************************************/

#define SCA_DSW_READ_RESPONSE                   0x8000      /* receive buffer full interrupt */
#define SCA_DSW_WRITE_RESPONSE                  0x4000      /* transmitter buffer empty interrupt */
#define SCA_DSW_CHECK                           0x2000      /* data overrun or character gap error */
#define SCA_DSW_TIMEOUT                         0x1000      /* timer interrupt, mode specific */
#define SCA_DSW_AUTOANSWER_REQUEST              0x0800      /* dataset is ringing and autoanswer is enabled */
#define SCA_DSW_BUSY                            0x0400      /* adapter is in either receive or transmit mode */
#define SCA_DSW_AUTOANSWER_ENABLED              0x0200      /* 1 when autoanswer mode has been enabled */
#define SCA_DSW_READY                           0x0100      /* Carrier detect? Connected and ready to rcv, xmit or sync */
#define SCA_DSW_RECEIVE_RUN                     0x0080      /* used in two-wire half-duplex STR mode only. "Slave" mode (?) */

#define IS_ONLINE(u) (((u)->flags & (UNIT_ATT|UNIT_DIS)) == UNIT_ATT)

typedef enum {              /*  ms     m = mode (0 = idle, 1 = send, 2 = receive), s = substate */
    SCA_STATE_IDLE          = 0x00, /* nothing happening */
    SCA_STATE_TURN_SEND     = 0x10, /* line turning around to the send state */
    SCA_STATE_SEND_SYNC     = 0x11, /* sca is sending syncs */
    SCA_STATE_SEND1         = 0x12, /* have issued write response, waiting for write command */
    SCA_STATE_SEND2         = 0x13, /* write command issued, "sending" byte */
    SCA_STATE_TURN_RECEIVE  = 0x20, /* line turnaround to the receive state */
    SCA_STATE_RECEIVE_SYNC  = 0x21, /* sca is receiving syncs */
    SCA_STATE_RECEIVE_SYNC2 = 0x22, /* bsc mode, waiting for 2nd SYN */
    SCA_STATE_RECEIVE_SYNC3 = 0x23, /* bsc mode, waiting for 1st non-SYN */
    SCA_STATE_RECEIVE1      = 0x24, /* "receiving" a byte */
    SCA_STATE_RECEIVE2      = 0x25, /* read response issued, "receiving" next byte */
} SCA_STATE;

#define in_send_state()    (sca_state & 0x10)
#define in_receive_state() (sca_state & 0x20)

static t_stat sca_svc           (UNIT *uptr);               /* prototypes */
static t_stat sca_reset         (DEVICE *dptr);
static t_stat sca_attach        (UNIT *uptr, CONST char *cptr);
static t_stat sca_detach        (UNIT *uptr);
static void sca_start_timer     (int n, int msec_now);
static void sca_halt_timer      (int n);
static void sca_toggle_timer    (int n, int msec_now);
                                                            /* timer states, chosen so any_timer_running can be calculated by oring states of all 3 timers */
typedef enum {SCA_TIMER_INACTIVE = 0, SCA_TIMER_RUNNING = 1, SCA_TIMER_INHIBITED = 2, SCA_TIMER_TIMEDOUT = 4} SCA_TIMER_STATE;

#define TIMER_3S        0       /* 3 second timer             index into sca_timer_xxx arrays */
#define TIMER_125S      1       /* 1.25 second timer */
#define TIMER_035S      2       /* 0.35 second timer */

static uint16 sca_dsw    = 0;                               /* device status word */
static uint32 sca_cwait  = 275;                             /* inter-character wait */
static uint32 sca_iwait  = 2750;                            /* idle wait */
static uint32 sca_state  = SCA_STATE_IDLE;
static uint8  sichar     = 0;                               /* sync/idle character */
static uint8  rcvd_char  = 0;                               /* most recently received character */
static uint8  sca_frame  = 8;
static char sca_port[3*CBUFSIZE];                             /* listening port */
static int32  sca_keepalive = 0;                            /* keepalive SYN packet period in msec, default = 0 (disabled) */
static SCA_TIMER_STATE sca_timer_state[3];                  /* current timer state */
static int    sca_timer_endtime[3];                         /* clocktime when timeout is to occur if state is RUNNING */
static int    sca_timer_timeleft[3];                        /* time left in msec if state is INHIBITED */
static t_bool any_timer_running = FALSE;                    /* TRUE if at least one timer is running */
static int    sca_timer_msec[3] = {3000, 1250, 350};        /* timebase in msec for the three timers: 3 sec, 1.25 sec, 0.35 sec */
static t_bool sca_timer_trigger;                            /* if TRUE, the "timer trigger" is set, the 0.35s timer is running and the 3 sec and 1.25 sec timers are inhibited */
static int    sca_nsyns  = 0;                               /* number of consecutively sent SYN's */
static int    idles_since_last_write = 0;                   /* used to detect when software has ceased sending data */
static SOCKET sca_lsock = INVALID_SOCKET;
static SOCKET sca_sock = INVALID_SOCKET;

#define SCA_SENDBUF_SIZE    145                             /* maximum number of bytes to buffer for transmission */
#define SCA_RCVBUF_SIZE     256                             /* max number of bytes to read from socket at a time */
#define SCA_SEND_THRESHHOLD 140                             /* number of bytes to buffer before initiating packet send */
#define SCA_IDLE_THRESHHOLD   3                             /* maximum number of unintentional idles to buffer before initiating send */

static uint8  sca_sendbuf[SCA_SENDBUF_SIZE];                /* bytes pending to write to socket */
static uint8  sca_rcvbuf[SCA_RCVBUF_SIZE];                  /* bytes received from socket, to be given to SCA */
static int    sca_n2send = 0;                               /* number of bytes queued for transmission */
static int    sca_nrcvd = 0;                                /* number of received bytes in buffer */
static int    sca_rcvptr = 0;                               /* index of next byte to take from rcvbuf */

#define UNIT_V_BISYNC     (UNIT_V_UF + 0)                   /* BSC (bisync) mode */
#define UNIT_V_BAUD       (UNIT_V_UF + 1)                   /* 3 bits for baud rate encoding */
#define UNIT_V_FULLDUPLEX (UNIT_V_UF + 4)
#define UNIT_V_AUTOANSWER (UNIT_V_UF + 5)
#define UNIT_V_LISTEN     (UNIT_V_UF + 6)                   /* listen socket mode */

#define UNIT_BISYNC       (1u << UNIT_V_BISYNC)
#define UNIT_BAUDMASK     (7u << UNIT_V_BAUD)
#define UNIT_BAUD600      (0u << UNIT_V_BAUD)
#define UNIT_BAUD1200     (1u << UNIT_V_BAUD)
#define UNIT_BAUD2000     (2u << UNIT_V_BAUD)
#define UNIT_BAUD2400     (3u << UNIT_V_BAUD)
#define UNIT_BAUD4800     (4u << UNIT_V_BAUD)
#define UNIT_FULLDUPLEX   (1u << UNIT_V_FULLDUPLEX)
#define UNIT_AUTOANSWER   (1u << UNIT_V_AUTOANSWER)
#define UNIT_LISTEN       (1u << UNIT_V_LISTEN)

t_stat sca_set_baud (UNIT *uptr, int32 value, CONST char *cptr, void *desc);

UNIT sca_unit = {                                                       /* default settings */
    UDATA (sca_svc, UNIT_ATTABLE|UNIT_BISYNC|UNIT_BAUD4800|UNIT_FULLDUPLEX, 0),
};

REG sca_reg[] = {                                                       /* DEVICE STATE/SETTABLE PARAMETERS: */
    { HRDATA (SCADSW,    sca_dsw,    16) },                             /* device status word */
    { DRDATA (SICHAR,    sichar,      8), PV_LEFT },                    /* sync/idle character */
    { DRDATA (RCVDCHAR,  rcvd_char,   8), PV_LEFT },                    /* most recently received character */
    { DRDATA (FRAME,     sca_frame,   8), PV_LEFT },                    /* frame bits (6, 7 or 8) */
    { DRDATA (SCASTATE,  sca_state,  32), PV_LEFT },                    /* current state */
    { DRDATA (CTIME,     sca_cwait,  32), PV_LEFT },                    /* inter-character wait */
    { DRDATA (ITIME,     sca_iwait,  32), PV_LEFT },                    /* idle wait (polling interval for socket connects) */
    { BRDATA (SCASOCKET, sca_port,   8, 8, sizeof(sca_port)) },         /* listening port number */
    { DRDATA (KEEPALIVE, sca_keepalive, 32), PV_LEFT },                 /* keepalive packet period in msec */
    { NULL }  };

MTAB sca_mod[] = {                                                      /* DEVICE OPTIONS */
    { UNIT_BISYNC,      0,                  "STR",  "STR",  NULL },     /* mode option */
    { UNIT_BISYNC,      UNIT_BISYNC,        "BSC",  "BSC",  NULL },
    { UNIT_BAUDMASK,    UNIT_BAUD600,       "600",  "600",  sca_set_baud },     /* data rate option */
    { UNIT_BAUDMASK,    UNIT_BAUD1200,      "1200", "1200", sca_set_baud },
    { UNIT_BAUDMASK,    UNIT_BAUD2000,      "2000", "2000", sca_set_baud },
    { UNIT_BAUDMASK,    UNIT_BAUD2400,      "2400", "2400", sca_set_baud },
    { UNIT_BAUDMASK,    UNIT_BAUD4800,      "4800", "4800", sca_set_baud },
    { UNIT_FULLDUPLEX,  0,                  "HALF", "HALF", NULL },     /* duplex option (does this matter?) */
    { UNIT_FULLDUPLEX,  UNIT_FULLDUPLEX,    "FULL", "FULL", NULL },
    { 0 }  };

DEVICE sca_dev = {
    "SCA", &sca_unit, sca_reg, sca_mod,
    1, 16, 16, 1, 16, 16,
    NULL, NULL, sca_reset,
    NULL, sca_attach, sca_detach
};

/*********************************************************************************************
 * sca_set_baud - set baud rate handler (SET SCA.BAUD nnn)
 *********************************************************************************************/

t_stat sca_set_baud (UNIT *uptr, int32 value, CONST char *cptr, void *desc)
{
    uint32 newbits;

    switch (value) {
        case 600:       newbits = UNIT_BAUD600;     break;
        case 1200:      newbits = UNIT_BAUD1200;    break;
        case 2000:      newbits = UNIT_BAUD2000;    break;
        case 2400:      newbits = UNIT_BAUD2400;    break;
        case 4800:      newbits = UNIT_BAUD4800;    break;
        default:        return SCPE_ARG;
    }

    CLRBIT(sca_unit.flags,  UNIT_BAUDMASK);
    SETBIT(sca_unit.flags, newbits);

    sca_cwait = 1320000 / value;            /* intercharacter wait time in instructions (roughly) */

    return SCPE_OK;
}

/*********************************************************************************************
 * HANDY MACROS
 *********************************************************************************************/

#define in_bsc_mode()           (sca_unit.flags  & UNIT_BISYNC)                 /* TRUE if user selected BSC mode */
#define in_str_mode()           ((sca_unit.flags & UNIT_BISYNC) == 0)           /* TRUE if user selected STR mode */

/*********************************************************************************************
 * mstring - allocate a copy of a string
 *********************************************************************************************/

char *mstring (const char *str)
{
    int len;
    char *m;

    len = strlen(str)+1;
    if ((m = (char *)malloc(len)) == NULL) {
        printf("Out of memory!");
        return (char *)"?";     /* this will of course cause trouble if it's subsequently freed */
    }
    strcpy(m, str);
    return m;
}

/*********************************************************************************************
 * sca_socket_error - call when there is an error reading from or writing to socket
 *********************************************************************************************/

static void sca_socket_error (void)
{
    char name[4*CBUFSIZE];

    /* print diagnostic? */
    printf("SCA socket error, closing connection\n");

    /* tell 1130 that connection was lost */
    CLRBIT(sca_dsw, SCA_DSW_READY);

    if (sca_sock != INVALID_SOCKET) {
        /* close socket, prepare to listen again if in listen mode. It's a "master" socket if it was an outgoing connection */
        sim_close_sock(sca_sock);
        sca_sock = INVALID_SOCKET;

        if (sca_unit.filename != NULL)                              /* reset filename string in unit record */
            free(sca_unit.filename);

        if (sca_unit.flags & UNIT_LISTEN) {
            name[sizeof (name) - 1] = '\0';
            snprintf(name, sizeof (name) - 1, "(Listening on port %s)", sca_port);
            sca_unit.filename = mstring(name);
            printf("%s\n", name);
        }
        else
            sca_unit.filename = mstring("(connection failed)");
    }

    /* clear buffers */
    sca_nrcvd = sca_rcvptr = sca_n2send = sca_nsyns = 0;
}

/*********************************************************************************************
 * sca_transmit_byte, sca_flush - send data buffering mechanism
 *********************************************************************************************/

static void sca_flush (void)
{
    int nbytes;

    if (sca_n2send > 0) {
#if (DEBUG_SCA & DEBUG_SCA_FLUSH)
        printf("* SCA_FLUSH %d byte%s\n", sca_n2send, (sca_n2send == 1) ? "" : "s");
#endif

        if (sca_sock != INVALID_SOCKET) {
            nbytes = sim_write_sock(sca_sock, (char *) sca_sendbuf, sca_n2send);

            if (nbytes == SOCKET_ERROR)
                sca_socket_error();
            else if (nbytes != sca_n2send)
                printf("SOCKET BLOCKED -- NEED TO REWRITE IBM1130_SCA.C");

            /* I'm going to assume that SCA communications on the 1130 will consist entirely */
            /* of back and forth exchanges of short records, and so we should never stuff the pipe so full that */
            /* it blocks. If it does ever block, we'll have to come up with an asychronous buffering mechanism. */
        }

        sca_n2send = 0;                     /* mark buffer cleared */
    }
}

/*********************************************************************************************
 * sca_transmit_byte - buffer a byte to be send to the socket
 *********************************************************************************************/

static void sca_transmit_byte (uint8 b)
{
    uint32 curtime;
    static uint32 last_syn_time, next_syn_time;

#if (DEBUG_SCA & DEBUG_SCA_TRANSMIT)
    printf("* SCA_TRANSMIT: %02x\n", b);
#endif

    /* write a byte to the socket. Let's assume an 8 bit frame in all cases.
     * We buffer them up, then send the packet when (a) it fills up to a certain point
     * and/or (b) some time has passed? We handle (b) by:
     *      checking in sva_svc if several sca_svc calls are made w/o any XIO_WRITES, and
     *      flushing send buffer on line turnaround, timeouts, or any other significant state change
     */

    /* on socket error, call sca_socket_error(); */

    if (b == sichar) {
        if (sca_nsyns >= MAX_SYNS) {            /* we're suppressing SYN's */
            if (sca_keepalive > 0) {            /* we're sending keepalives, though... check to see if it's time */
                curtime = sim_os_msec();
                if (curtime >= next_syn_time || curtime < last_syn_time) {      /*  check for < last_syn_time because sim_os_msec() can wrap when OS has been running a long time */
                    sca_sendbuf[sca_n2send++] = b;
                    sca_sendbuf[sca_n2send++] = b;                              /* send 2 of them */
                    sca_flush();
                    last_syn_time = curtime;
                    next_syn_time = last_syn_time + sca_keepalive;
                }
            }
            return;
        }

        if (++sca_nsyns == MAX_SYNS) {          /* we've sent a bunch of SYN's, time to stop sending them */
            sca_sendbuf[sca_n2send] = b;        /* send last one */
            sca_flush();
            last_syn_time = sim_os_msec();      /* remember time, and note time to send next one */
            next_syn_time = last_syn_time + sca_keepalive;
            return;
        }
    }
    else
        sca_nsyns = 0;

    sca_sendbuf[sca_n2send] = b;                /* store character */

    if (++sca_n2send >= SCA_SEND_THRESHHOLD)
        sca_flush();                            /* buffer is full, send it immediately */
}

/*********************************************************************************************
 * sca_interrupt (utility routine) - set a bit in the device status word and initiate an interrupt
 *********************************************************************************************/

static void sca_interrupt (int bit)
{
    sca_dsw |= bit;                     /* set device status word bit(s) */
    SETBIT(ILSW[1], ILSW_1_SCA);        /* set interrupt level status word bit */

    calc_ints();                        /* udpate simulator interrupt status (not really needed if within xio handler, since ibm1130_cpu calls it after xio handler) */
}

/*********************************************************************************************
 * sca_reset - reset the SCA device
 *********************************************************************************************/

static t_stat sca_reset (DEVICE *dptr)
{
    /* flush any pending data */
    sca_flush();
    sca_nrcvd = sca_rcvptr = sca_n2send = sca_nsyns = 0;

    /* reset sca activity */
    sca_state = SCA_STATE_IDLE;
    CLRBIT(sca_dsw, SCA_DSW_BUSY | SCA_DSW_AUTOANSWER_ENABLED | SCA_DSW_RECEIVE_RUN | SCA_DSW_READ_RESPONSE | SCA_DSW_WRITE_RESPONSE | SCA_DSW_CHECK | SCA_DSW_TIMEOUT | SCA_DSW_AUTOANSWER_REQUEST);
    sca_timer_state[0] = sca_timer_state[1] = sca_timer_state[2] = SCA_TIMER_INACTIVE;
    any_timer_running = FALSE;
    sca_timer_trigger = FALSE;

    if (sca_unit.flags & UNIT_ATT)                  /* if unit is attached (or listening) */
        sim_activate(&sca_unit, sca_iwait);         /* poll for service. Must do this here as BOOT clears activity queue before resetting all devices */

    return SCPE_OK;
}

/*********************************************************************************************
 * sca_attach - attach the SCA device
 *********************************************************************************************/

static t_stat sca_attach (UNIT *uptr, CONST char *cptr)
{
    char host[CBUFSIZE], port[CBUFSIZE];
    t_bool do_listen;
    char name[4*CBUFSIZE];
    t_stat r;

    do_listen = sim_switches & SWMASK('L');     /* -l means listen mode */

    if (sca_unit.flags & UNIT_ATT)              /* if already attached, detach */
        detach_unit(&sca_unit);

    if (do_listen) {                            /* if listen mode, string specifies port number (only; otherwise it's a dummy argument) */
        if (sim_parse_addr (cptr, host, sizeof(host), NULL, port, sizeof(port), SCA_DEFAULT_PORT, NULL))
            return SCPE_ARG;
        if ((0 == strcmp(port, cptr)) && (0 == strcmp(port, "dummy")))
            strlcpy(port, SCA_DEFAULT_PORT, sizeof (port));

        snprintf(sca_port, sizeof (sca_port) - 1, "%s%s%s:%s", strchr(host, ':') ? "[" : "", host, strchr(host, ':') ? "]" : "", port);

        /* else if nondigits specified, ignore... but the command has to have something there otherwise the core scp */
        /* attach_cmd() routine complains "too few arguments". */

        sca_lsock = sim_master_sock(sca_port, &r);
        if (r != SCPE_OK)
            return r;
        if (sca_lsock == INVALID_SOCKET)
            return SCPE_OPENERR;

        SETBIT(sca_unit.flags, UNIT_LISTEN);    /* note that we are listening, not yet connected */

        name[sizeof (name) - 1] = '\0';
        snprintf(name, sizeof (name) - 1, "(Listening on port %s)", sca_port);
        sca_unit.filename = mstring(name);
        printf("%s\n", sca_unit.filename);

    }
    else {
        while (*cptr && *cptr <= ' ')
            cptr++;

        if (! *cptr)
            return SCPE_2FARG;

        if (sim_parse_addr (cptr, host, sizeof(host), NULL, port, sizeof(port), SCA_DEFAULT_PORT, NULL))
            return SCPE_ARG;
        if ((0 == strcmp(cptr, port)) && (0 == strcmp(host, ""))) {
            strlcpy(host, port, sizeof (host));
            strlcpy(port, SCA_DEFAULT_PORT, sizeof (port));
        }

        snprintf(sca_port, sizeof (sca_port) - 1, "%s%s%s:%s", strchr(host, ':') ? "[" : "", host, strchr(host, ':') ? "]" : "", port);

        if ((sca_sock = sim_connect_sock(sca_port, NULL, NULL)) == INVALID_SOCKET)
            return SCPE_OPENERR;

        /* sim_connect_sock() sets socket to nonblocking before initiating the connect, so
         * the connect is pending when it returns. For outgoing connections, the attach command should wait
         * until the connection succeeds or fails. We use "sim_check_conn" to wait and find out which way it goes...
         */

        while (0 == sim_check_conn(sca_sock, 0))/* wait for connection to complete or fail */
            sim_os_ms_sleep(1000);

        if (1 == sim_check_conn(sca_sock, 0)) { /* sca_sock appears in "writable" set -- connect completed */
            name[sizeof (name) - 1] = '\0';
            snprintf(name, sizeof (name) - 1, "%s%s%s:%s", strchr(host, ':') ? "[" : "", host, strchr(host, ':') ? "]" : "", port);
            sca_unit.filename = mstring(name);
            SETBIT(sca_dsw, SCA_DSW_READY);
        }
        else {                                  /* sca_sock appears in "error" set -- connect failed */
            sim_close_sock(sca_sock);
            sca_sock = INVALID_SOCKET;
            return SCPE_OPENERR;
        }
    }

    /* set up socket connect or listen. on success, set UNIT_ATT.
     * If listen mode, set UNIT_LISTEN. sca_svc will poll for connection
     * If connect mode, set dsw SCA_DSW_READY to indicate connection is up
     */

    SETBIT(sca_unit.flags, UNIT_ATT);                   /* record successful socket binding */

    sca_state = SCA_STATE_IDLE;
    sim_activate(&sca_unit, sca_iwait);                 /* start polling for service */

    sca_n2send = 0;                                     /* clear buffers */
    sca_nrcvd  = 0;
    sca_rcvptr = 0;
    sca_nsyns  = 0;

    return SCPE_OK;
}

/*********************************************************************************************
 * sca_detach - detach the SCA device
 *********************************************************************************************/

static t_stat sca_detach (UNIT *uptr)
{
    if ((sca_unit.flags & UNIT_ATT) == 0)
        return SCPE_OK;

    sca_flush();

    sca_state = SCA_STATE_IDLE;                 /* stop processing during service calls */
    sim_cancel(&sca_unit);                      /* stop polling for service */

    CLRBIT(sca_dsw, SCA_DSW_READY);             /* indicate offline */

    if (sca_sock != INVALID_SOCKET) {           /* close connected socket */
        sim_close_sock(sca_sock);
        sca_sock = INVALID_SOCKET;
    }
    if (sca_lsock != INVALID_SOCKET) {          /* close listening socket */
        sim_close_sock(sca_lsock);
        sca_lsock = INVALID_SOCKET;
    }

    free(sca_unit.filename);
    sca_unit.filename = NULL;

    CLRBIT(sca_unit.flags, UNIT_ATT|UNIT_LISTEN);

    return SCPE_OK;
}

/*********************************************************************************************
 * sca_check_connect - see if an incoming socket connection has com
 *********************************************************************************************/

static void sca_check_connect (void)
{
    char *connectaddress;

    if ((sca_sock = sim_accept_conn(sca_lsock, &connectaddress)) == INVALID_SOCKET)
        return;

    printf("(SCA connection from %s)\n", connectaddress);

    if (sca_unit.filename != NULL)
        free(sca_unit.filename);

    sca_unit.filename = connectaddress;

    SETBIT(sca_dsw, SCA_DSW_READY);                     /* indicate active connection  */

    if (sca_dsw & SCA_DSW_AUTOANSWER_ENABLED)           /* if autoanswer was enabled, I guess we should give them an interrupt. Untested. */
        sca_interrupt(SCA_DSW_AUTOANSWER_REQUEST);
}

/*********************************************************************************************
 *  sca_check_indata - try to fill receive buffer from socket
 *********************************************************************************************/

static void sca_check_indata (void)
{
    int nbytes;

    sca_rcvptr = 0;                             /* reset pointers and count */
    sca_nrcvd  = 0;

#ifdef FAKE_SCA

    nbytes = 5;                                 /* FAKE: receive SYN SYN SYN SYN DLE ACK0 */
    sca_rcvbuf[0] = 0x32;
    sca_rcvbuf[1] = 0x32;
    sca_rcvbuf[2] = 0x32;
    sca_rcvbuf[3] = 0x10;
    sca_rcvbuf[4] = 0x70;

#else
                                                /* read socket; 0 is returned if no data is available */
    nbytes = sim_read_sock(sca_sock, (char *) sca_rcvbuf, SCA_RCVBUF_SIZE);

#endif

    if (nbytes < 0)
        sca_socket_error();
    else                                        /* zero or more */
        sca_nrcvd = nbytes;

#if (DEBUG_SCA & DEBUG_SCA_CHECK_INDATA)
    if (sca_nrcvd > 0)
        printf("* SCA_CHECK_INDATA %d byte%s\n", sca_nrcvd, (sca_nrcvd == 1) ? "" : "s");
#endif
}

/*********************************************************************************************
 * sca_svc - handled scheduled event. This will presumably be scheduled frequently, and can check
 * for incoming data, reasonableness of initiating a write interrupt, timeouts etc.
 *********************************************************************************************/

static t_stat sca_svc (UNIT *uptr)
{
    t_bool timeout;
    int msec_now;
    int i;

    /* if not connected, and if in wait-for-connection mode, check for connection attempt */
    if ((sca_unit.flags & UNIT_LISTEN) && ! (sca_dsw & SCA_DSW_READY))
        sca_check_connect();

    if (any_timer_running) {
        msec_now = sim_os_msec();

        timeout = FALSE;
        for (i = 0; i < 3; i++) {
            if (sca_timer_state[i] == SCA_TIMER_RUNNING && msec_now >= sca_timer_endtime[i]) {
                timeout = TRUE;
                sca_timer_state[i] = SCA_TIMER_TIMEDOUT;
#if (DEBUG_SCA & DEBUG_SCA_TIMERS)
                printf("+ SCA_TIMER %d timed out\n", i);
#endif

                if (i == TIMER_035S && sca_timer_trigger) {
                    sca_timer_trigger = FALSE;                                      /* uninhibit the other two timers */
                    sca_toggle_timer(TIMER_3S,   msec_now);
                    sca_toggle_timer(TIMER_125S, msec_now);
                }
            }
        }

        if (timeout)
            sca_interrupt(SCA_DSW_TIMEOUT);

        any_timer_running = (sca_timer_state[0]| sca_timer_state[1] | sca_timer_state[2]) & SCA_TIMER_RUNNING;
    }

    if (sca_dsw & SCA_DSW_READY) {              /* if connected */

        /* if rcvd data buffer is empty, and if in one of the receive states, check for arrival of received data */
        if (in_receive_state() && sca_rcvptr >= sca_nrcvd)
            sca_check_indata();

        switch (sca_state) {
            case SCA_STATE_IDLE:
                break;

            case SCA_STATE_TURN_SEND:
                /* has enough time gone by yet? if so... */
                sca_state = SCA_STATE_SEND1;
                sca_interrupt(SCA_DSW_WRITE_RESPONSE);
                break;

            case SCA_STATE_SEND_SYNC:
                sca_transmit_byte(sichar);
                break;

            case SCA_STATE_SEND1:
                sca_transmit_byte(sichar);          /* character interval has passed with no character written? character gap check */
                sca_interrupt(SCA_DSW_CHECK);       /* send an idle character (maybe? for socket IO maybe send nothing) and interrupt */

                if (idles_since_last_write >= 0) {
                    if (++idles_since_last_write >= SCA_IDLE_THRESHHOLD) {
                        sca_flush();
                        idles_since_last_write = -1;    /* don't send a short packet again until real data gets written again */
                        sca_nsyns = 0;                  /* resume sending syns if output starts up again */
                    }
                }
                break;

            case SCA_STATE_SEND2:
                sca_state = SCA_STATE_SEND1;            /* character has been sent. Schedule another transmit */
                sca_interrupt(SCA_DSW_WRITE_RESPONSE);
                break;

            case SCA_STATE_TURN_RECEIVE:
                /* has enough time gone by yet? if so... */
                sca_state = SCA_STATE_RECEIVE_SYNC;     /* assume a character is coming in */
                break;

            case SCA_STATE_RECEIVE_SYNC:
                if (sca_rcvptr < sca_nrcvd) {
                    rcvd_char = sca_rcvbuf[sca_rcvptr++];
#if (DEBUG_SCA & DEBUG_SCA_RECEIVE_SYNC)
                    printf("* SCA rcvd %02x %s\n", rcvd_char, (rcvd_char == sichar) ? "sync1" : "ignored");
#endif
                    if (in_bsc_mode()) {
                        if (rcvd_char == sichar)            /* count the SYN but don't interrupt */
                            sca_state = SCA_STATE_RECEIVE_SYNC2;
                    }
                }
                break;

            case SCA_STATE_RECEIVE_SYNC2:
                if (sca_rcvptr < sca_nrcvd) {
                    rcvd_char = sca_rcvbuf[sca_rcvptr++];
#if (DEBUG_SCA & DEBUG_SCA_RECEIVE_SYNC)
                    printf("* SCA rcvd %02x %s\n", rcvd_char, (rcvd_char == sichar) ? "sync2" : "ignored");
#endif
                    if (in_bsc_mode()) {
                        if (rcvd_char == sichar)            /* count the SYN but don't interrupt */
                            sca_state = SCA_STATE_RECEIVE_SYNC3;
                    }
                }
                break;

            case SCA_STATE_RECEIVE_SYNC3:
            case SCA_STATE_RECEIVE1:
                if (sca_rcvptr < sca_nrcvd) {
                    rcvd_char = sca_rcvbuf[sca_rcvptr++];

                    if (sca_state == SCA_STATE_RECEIVE_SYNC3 && rcvd_char == sichar) {
                        /* we're ready for data, but we're still seeing SYNs */
#if (DEBUG_SCA & DEBUG_SCA_RECEIVE_SYNC)
                        printf("* SCA rcvd %02x extra sync\n", rcvd_char);
#endif
                    }
                    else {
#if (DEBUG_SCA & DEBUG_SCA_RECEIVE_DATA)
                        printf("* SCA rcvd %02x\n", rcvd_char);
#endif
                        sca_interrupt(SCA_DSW_READ_RESPONSE);
                        sca_state = SCA_STATE_RECEIVE2;
                    }
                }
                /* otherwise remain in state until data becomes available */
                break;

            case SCA_STATE_RECEIVE2:                    /* if we are still in this state when another service interval has passed */
                if (sca_rcvptr < sca_nrcvd) {
                    rcvd_char = sca_rcvbuf[sca_rcvptr++];

                    sca_interrupt(SCA_DSW_CHECK);       /* overrun error */
                    sca_state = SCA_STATE_RECEIVE1;     /* another character will come soon */
                }
                break;

            default:
                printf("Simulator error: unknown state %d in sca_svc\n", sca_state);
                sca_state = SCA_STATE_IDLE;
                break;
        }
    }
                                                /* schedule service again */
    sim_activate(&sca_unit, (sca_state == SCA_STATE_IDLE) ? sca_iwait : sca_cwait);

    return SCPE_OK;
}

/*********************************************************************************************
 * sca_toggle_timer - toggle a given timer's running/inhibited state for XIO_CONTROL
 *********************************************************************************************/

static void sca_toggle_timer (int n, int msec_now)
{
    if (sca_timer_state[n] == SCA_TIMER_RUNNING && sca_timer_trigger) {
        sca_timer_state[n] = SCA_TIMER_INHIBITED;
        sca_timer_timeleft[n] = sca_timer_endtime[n] - msec_now;        /* save time left */
#if (DEBUG_SCA & DEBUG_SCA_TIMERS)
        printf("+ SCA_TIMER %d inhibited\n", n);
#endif
    }
    else if (sca_timer_state[n] == SCA_TIMER_INHIBITED && ! sca_timer_trigger) {
        sca_timer_state[n] = SCA_TIMER_RUNNING;
        sca_timer_endtime[n] = sca_timer_timeleft[n] + msec_now;        /* compute new endtime */
#if (DEBUG_SCA & DEBUG_SCA_TIMERS)
        printf("+ SCA_TIMER %d uninhibited\n", n);
#endif
    }
}

static void sca_start_timer (int n, int msec_now)
{
    sca_timer_state[n]   = SCA_TIMER_RUNNING;
    sca_timer_endtime[n] = sca_timer_msec[n] + msec_now;
    any_timer_running    = TRUE;
#if (DEBUG_SCA & DEBUG_SCA_TIMERS)
    printf("+ SCA_TIMER %d started\n", n);
#endif
}

static void sca_halt_timer (int n)
{
#if (DEBUG_SCA & DEBUG_SCA_TIMERS)
    if (sca_timer_state[n] != SCA_TIMER_INACTIVE)
        printf("+ SCA_TIMER %d stopped\n", n);
#endif

    sca_timer_state[n] = SCA_TIMER_INACTIVE;
}

/*********************************************************************************************
 * sca_start_transmit - initiate transmit mode, from XIO_INITR or XIO_CONTROL (sync mode)
 *********************************************************************************************/

void sca_start_transmit (int32 iocc_addr, int32 modify)
{
    sca_flush();
    sca_nsyns = 0;                          /* reset SYN suppression */

    /* Address bits are used to reset DSW conditions. */

    if (modify & 0x40)                      /* bit 9. If set, reset all conditions */
        iocc_addr = 0xD800;

    iocc_addr &= 0xD800;                    /* look at just bits 0, 1, 3 and 4 */
    if (iocc_addr) {                        /* if set, these bits clear DSW conditions */
        CLRBIT(sca_dsw, iocc_addr);
        CLRBIT(ILSW[1], ILSW_1_SCA);        /* and I assume clear the interrupt condition too? (Seems unlikely that INITW would */
    }                                       /* be used in an interrupt service routine before SENSE, but who knows?) */

    if (! in_send_state()) {
        sca_state = SCA_STATE_TURN_SEND;    /* begin line turnaround */
    }
    else {
        sca_state = SCA_STATE_SEND1;        /* line is  */
        sca_interrupt(SCA_DSW_WRITE_RESPONSE);
    }

    SETBIT(sca_dsw, SCA_DSW_BUSY);          /* SCA is now busy, in transmit mode */

    sim_cancel(&sca_unit);
    sim_activate(&sca_unit, sca_cwait);     /* start polling frequently */
}

/*********************************************************************************************
 * xio_sca - handle SCA XIO command
 *********************************************************************************************/

void xio_sca (int32 iocc_addr, int32 func, int32 modify)
{
    char msg[80];
    int i, msec_now;

    switch (func) {
        case XIO_READ:                              /* ***** XIO_READ - store last-received character to memory */
#if (DEBUG_SCA & DEBUG_SCA_XIO_READ)
            printf("SCA RD  addr %04x mod %02x rcvd_char %02x\n", iocc_addr, modify, rcvd_char);
#endif
            if (modify & 0x03) {                    /* bits 14 and 15 */
#if (DEBUG_SCA & DEBUG_SCA_XIO_READ)
                printf("(rd diag)\n");
#endif
                /* if either of low two modifier bits is set, reads diagnostic words. whatever that is */
            }
            else {
                WriteW(iocc_addr, rcvd_char << 8);  /* always return last received character */

                /* note: in read mode, read w/o interrupt (or two reads after an interrupt) causes a check
                 * so here we have to check the current state and possibly cause an interrupt
                 * Failure to have read before another arrives (overrun) also causes a check.
                 */

                if (sca_state == SCA_STATE_RECEIVE2)/* XIO_READ should only be done (and only once) after a character interrupt */
                    sca_state = SCA_STATE_RECEIVE1; /* assume another character is coming in -- wait for it */
                else
                    sca_interrupt(SCA_DSW_CHECK);
            }
            break;

        case XIO_WRITE:                             /* ***** XIO_WRITE - transfer character from memory to output shift register */
#if (DEBUG_SCA & DEBUG_SCA_XIO_WRITE)
            printf("SCA WRT addr %04x (%04x) mod %02x\n", iocc_addr, M[iocc_addr & mem_mask], modify);
#endif
            if (modify & 0x01) {                    /* bit 15 */
                /* clear audible alarm trigger */
#if (DEBUG_SCA & DEBUG_SCA_XIO_WRITE)
                printf("(clr audible alarm trigger)\n");
#endif
            }
            /* else? or can they all operate in parallel? */
            if (modify & 0x02) {                    /* bit 14 */
                /* set audible alarm trigger */
#if (DEBUG_SCA & DEBUG_SCA_XIO_WRITE)
                printf("(set audible alarm trigger)\n");
#endif
            }
            /* else? */
            if (modify & 0x04) {                    /* bit 13 */
#if (DEBUG_SCA & DEBUG_SCA_XIO_WRITE)
                printf("(set SYN)\n");
#endif
                /* set sync/idle character */
                sichar = (uint8) (ReadW(iocc_addr) >> 8);
                sca_nsyns = 0;                      /* reset SYN suppression */
            }
            /* else? does presence of mod bit preclude sending a character? */
            if ((modify & 0x07) == 0) {             /* no modifiers */
                /* transmit character --
                 * note: in write mode, failure to write soon enough after a write response interrupt causes a check
                 * Also, writing w/o interrupt (or two writes after an interrupt) causes a check
                 * so again, here we have to check the state to be sure that a write is appropriate
                 *
                 * note that in this simulator, we transmit the character immediately on XIO_WRITE. Therefore,
                 * there is no need to delay an end-operation function (XIO_CONTROL bit 13) until after a character time
                 */

                idles_since_last_write = 0;

                switch (sca_state) {
                    case SCA_STATE_SEND_SYNC:
                    case SCA_STATE_SEND1:
                        sca_transmit_byte((uint8) (M[iocc_addr & mem_mask] >> 8));
                        sca_state = SCA_STATE_SEND2;
                        sim_cancel(&sca_unit);
                        sim_activate(&sca_unit, sca_cwait);     /* schedule service after character write time */
                        break;

                    case SCA_STATE_SEND2:
                        sca_interrupt(SCA_DSW_CHECK);           /* write issued while a character is in progress out? write overrun */
                        break;

                    case SCA_STATE_IDLE:                        /* wrong time to issue a write, incorrect sca state */
                    default:
                        sca_flush();
                        sca_interrupt(SCA_DSW_CHECK);           /* ??? or does this just perform a line turnaround and start transmission? */
                        break;
                }

            }
            break;

        case XIO_CONTROL:                           /* ***** XIO_CONTROL - manipulate interface state */
#if (DEBUG_SCA & DEBUG_SCA_XIO_CONTROL)
            printf("SCA CTL addr %04x mod %02x\n", iocc_addr, modify);
#endif
            if (modify & 0x80) {                    /* bit 8 */
                /* enable auto answer */
#if (DEBUG_SCA & DEBUG_SCA_XIO_CONTROL)
                printf("(enable autoanswer)\n");
#endif
                SETBIT(sca_unit.flags, UNIT_AUTOANSWER);
                SETBIT(sca_dsw, SCA_DSW_AUTOANSWER_ENABLED);
            }

            if (modify & 0x40) {                    /* bit 9 */
                /* disable auto answer */
#if (DEBUG_SCA & DEBUG_SCA_XIO_CONTROL)
                printf("(disable autoanswer)\n");
#endif
                CLRBIT(sca_unit.flags, UNIT_AUTOANSWER);
                CLRBIT(sca_dsw, SCA_DSW_AUTOANSWER_ENABLED);
            }

            if (modify & 0x20) {                    /* bit 10 */
                /* toggle timers, inhibit->run or run->inhibit */
#if (DEBUG_SCA & (DEBUG_SCA_XIO_CONTROL|DEBUG_SCA_TIMERS))
                printf("(toggle timers)\n");
#endif
                msec_now = sim_os_msec();

                if (in_bsc_mode()) {
                    sca_timer_trigger = ! sca_timer_trigger;    /* toggle the timer trigger */
                    if (sca_timer_trigger)                      /* if we've just set it, we're stopping the other timers and  */
                        sca_start_timer(TIMER_035S, msec_now);  /* starting the 0.35 sec timer */
                    else
                        sca_halt_timer(TIMER_035S);
                }

                sca_toggle_timer(TIMER_3S, msec_now);           /* toggle the 3 sec and 1.35 sec timers accordingly */
                sca_toggle_timer(TIMER_125S, msec_now);

                any_timer_running = (sca_timer_state[0]| sca_timer_state[1] | sca_timer_state[2]) & SCA_TIMER_RUNNING;
            }

            if (modify & 0x10) {                    /* bit 11 */
                /* enable sync mode. See references to this in FC manual
                 * In STR mode only, sends a constant stream of SYN's without any CPU intervention
                 * In BSC mode, appears to start the 1.25 second timer and otherwise act like INITW?
                 */
#if (DEBUG_SCA & DEBUG_SCA_XIO_CONTROL)
                printf("(enable sync mode)\n");
#endif

                if (in_bsc_mode()) {                /* in bsc mode start the 1.25 second timer. not sure what resets it?!? */
                    if (! in_send_state())          /* also may cause a line turnaround */
                        sca_start_transmit(iocc_addr, 0);

                    sca_start_timer(TIMER_125S, sim_os_msec());
                }
            }

            if (modify & 0x08) {                    /* bit 12 */
                /* diagnostic mode. What does this do?!? */
#if (DEBUG_SCA & DEBUG_SCA_XIO_CONTROL)
                printf("(diag mode)\n");
#endif
            }

            if (modify & 0x04) {                    /* bit 13 */
                /* end operation (reset adapter. See references to this in FC manual). In transmit mode the real adapter delays this
                 * function until current character has been sent. We don't need to do that as the character got buffered for transmission
                 * immediately on XIO_WRITE.
                 */

#if (DEBUG_SCA & (DEBUG_SCA_XIO_CONTROL|DEBUG_SCA_XIO_INITR|DEBUG_SCA_XIO_INITW))
                printf("(end operation)\n");
#endif
                sca_state = SCA_STATE_IDLE;
                sca_timer_state[0] = sca_timer_state[1] = sca_timer_state[2] = SCA_TIMER_INACTIVE;
                any_timer_running = FALSE;
                sca_timer_trigger = FALSE;
                sca_nsyns = 0;                      /* reset SYN suppression */
                CLRBIT(sca_dsw, SCA_DSW_BUSY);
            }

            if (modify & 0x02) {                    /* bit 14 */
#if (DEBUG_SCA & DEBUG_SCA_XIO_CONTROL)
                printf("(6 bit frame)\n");
#endif
                /* set six bit character frame. This is reset to 8 bits at every line turnaround */
                sca_frame = 6;
            }

            if (modify & 0x01) {                    /* bit 15 */
#if (DEBUG_SCA & DEBUG_SCA_XIO_CONTROL)
                printf("(7 bit frame)\n");
#endif
                /* set seven bit character frame. This is reset to 8 bits at every line turnaround */
                sca_frame = 7;
            }

            sca_flush();
            break;

        case XIO_INITW:                             /* ***** XIO_INITW - put SCA in transmit mode */
#if (DEBUG_SCA & DEBUG_SCA_XIO_INITW)
            printf("SCA INITW addr %04x mod %02x\n", iocc_addr, modify);
#endif
            /* enter transmit mode. Causes line turnaround. Resets frame to 8 bits. */
            /* (may cause syncing, may involve a fixed timeout?) */
            sca_frame = 8;
            sca_start_transmit(iocc_addr, modify);  /* this code pulled out to a subroutine cuz transmit can be started from XIO_CONTROL too */
            break;

        case XIO_INITR:                             /* ***** XIO_INITR - put SCA in receive mode */
#if (DEBUG_SCA & DEBUG_SCA_XIO_INITR)
            printf("SCA INITR addr %04x mod %02x\n", iocc_addr, modify);
#endif
            sca_flush();

            sca_nrcvd = sca_rcvptr = 0;             /* discard any data previously read! */
            sca_nsyns = 0;                          /* reset SYN suppression */

            /* enter receive mode. Causes line turnaround (e.g. resets to 8 bit frame). Modifier bits are used here too */
            /* (may cause syncing, may involve a fixed timeout?) */

            sca_frame = 8;
            if (! in_receive_state())
                sca_state = SCA_STATE_TURN_RECEIVE; /* begin line turnaround */
            else
                sca_state = SCA_STATE_RECEIVE_SYNC;

            SETBIT(sca_dsw, SCA_DSW_BUSY);          /* SCA is now busy, in receive mode */

            if (in_bsc_mode())                      /* in BSC mode, start the 3 second timer when we enter receive mode */
                sca_start_timer(TIMER_3S, sim_os_msec());

            break;

        case XIO_SENSE_DEV:                         /* ***** XIO_SENSE_DEV - read device status word */
#if (DEBUG_SCA & DEBUG_SCA_XIO_SENSE_DEV)
            printf("SCA SNS mod %02x dsw %04x\n", modify, sca_dsw);
#endif
            ACC = sca_dsw;                          /* return DSW in accumulator */

            if (modify & 0x01) {                    /* bit 15: reset interrupts */
#if (DEBUG_SCA & DEBUG_SCA_XIO_SENSE_DEV)
                printf("(reset interrupts)\n");
#endif
                CLRBIT(sca_dsw, SCA_DSW_READ_RESPONSE | SCA_DSW_WRITE_RESPONSE | SCA_DSW_CHECK | SCA_DSW_TIMEOUT | SCA_DSW_AUTOANSWER_REQUEST);
                CLRBIT(ILSW[1], ILSW_1_SCA);
            }

            if (modify & 0x02) {                    /* bit 14: restart running timers */
#if (DEBUG_SCA & (DEBUG_SCA_XIO_SENSE_DEV|DEBUG_SCA_TIMERS))
                printf("(restart timers)\n");
#endif
                msec_now = sim_os_msec();           /* restart "any running timer?" All three, really? */
                for (i = 0; i < 3; i++)
                    if (sca_timer_state[i] == SCA_TIMER_RUNNING || sca_timer_state[i] == SCA_TIMER_TIMEDOUT)
                        sca_start_timer(i, msec_now);
            }
            break;

        default:
            sprintf(msg, "Invalid SCA XIO function %x", func);
            xio_error(msg);
    }
}
